package solver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import model.Neighborhood;
import model.Process;
import model.Service;
import model.SolverEnvironment;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import util.ObjectIdPropertyPair;

import etm.core.configuration.EtmManager;
import etm.core.monitor.EtmMonitor;
import etm.core.monitor.EtmPoint;
import gurobi.GRBVar;

public class SelectionAgent5 extends SelectionAgent4 {
	private static Logger log = LoggerFactory.getLogger(SelectionAgent5.class);

	// private static final EtmMonitor etmMonitor = EtmManager.getEtmMonitor();

	public SelectionAgent5(SolverEnvironment env) {
		super(env);
	}

	public List<Integer> selectAllProcessesofAHeavyMachinesFast(int nop,
			int noem, int non) {
		// EtmPoint point = etmMonitor
		// .createPoint("SelectionAgent4:selectFullRandomProcessesAndMachinesFast");
		noem = noem * factor;
		nop = nop < env.problem.P ? nop : env.problem.P;
		noem = noem < env.problem.M ? noem : env.problem.M;
		List<Integer> selectedMachines = new ArrayList<Integer>();
		List<Integer> selectedNeighborHoods = RobertFloydSamplingAlgorithm(
				env.problem.N, non);
		List<Integer> ballotProcesses = new ArrayList<Integer>();
		for (Integer n_id : selectedNeighborHoods) {
			Neighborhood neighborhood = env.problem.getNeighborhoods()
					.get(n_id);
			int r = 0;
			if (selectedNeighborHoods.indexOf(n_id) == selectedNeighborHoods
					.size() - 1) {
				r = noem % non;
			}
			selectedMachines.addAll(neighborhood.getMachines());
		}

		selectedMachines = filterKeepingSymmetricallyLoadedMachines(
				selectedMachines, noem / factor);

		for (Integer m_id : selectedMachines) {
			ballotProcesses.addAll(env.getCurrentSolution()
					.getProcessesForMachine(m_id));
			if (ballotProcesses.size() > nop)
				break;
		}

		// if (env.getRandom().nextDouble() < 0.95)
		ballotProcesses = filterKeepingCostlyHeavyProcesses(ballotProcesses,
				nop);

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		for (Integer id : RobertFloydSamplingAlgorithm(ballotProcesses.size(),
				nop)) {
			Process aProcess = env.problem.getProcesses().get(
					ballotProcesses.get(id));
			selectedProcesses.add(aProcess.getId());
			aProcess.clearEligibleMachines();
			aProcess.setEligibleMachines(new ArrayList<Integer>(
					selectedMachines));
		}
		clearConflictConstraintsForFixedProcess(selectedProcesses);
		clearTransientConstraints(selectedProcesses);
		if (!dependencyConstraintsNeeded) {
			clearDependencies1(selectedProcesses);
			clearDependencies2(selectedProcesses);
		}
		reduceXVars(selectedProcesses);
		clearSelectedProcessesHavingOneEligibleMachine(selectedProcesses);
		logSubproblemStatistics(selectedProcesses);
		// point.collect();
		return selectedProcesses;
	}

	private List<Integer> filterKeepingCostlyHeavyProcesses(
			List<Integer> ballotProcesses, int nop) {
		// int before = ballotProcesses.size();
		List<ObjectIdPropertyPair> resource_demands = new ArrayList<ObjectIdPropertyPair>(
				ballotProcesses.size());
		// int selectedR =
		// env.getPotentialSolution().load_cost_max_contribution_resource;
		int selectedR = env.getRandom().nextInt(env.getProblem().RE);
		for (Integer p : ballotProcesses) {
			resource_demands.add(new ObjectIdPropertyPair(p, env.getProblem()
					.getR(p, selectedR)));
		}
		Collections.sort(resource_demands);
		// System.err.println(resource_demands);
		resource_demands = resource_demands.subList(0,
				Math.min(2 * nop, resource_demands.size()));
		// System.err.println(resource_demands);
		ballotProcesses.clear();
		for (ObjectIdPropertyPair p : resource_demands)
			ballotProcesses.add(p.getObjectId());
		// int after = ballotProcesses.size();
		// log.debug(String.format("Processes before=%d after=%d", before,
		// after));
		return ballotProcesses;
	}

	protected List<Integer> filterKeepingSymmetricallyLoadedMachines(
			List<Integer> selectedMachines, int machines_to_keep) {
		env.getCurrentSolution().refreshLoadCostContribution(selectedMachines);
		env.getCurrentSolution().refreshBalanceCostContribution(
				selectedMachines);

		long[] cost_per_machine = new long[selectedMachines.size()];
		int i = 0;
		for (Integer m_id : selectedMachines) {
			cost_per_machine[i] = (env.getCurrentSolution().load_cost_contribution_zipped[m_id] + env
					.getCurrentSolution().balance_cost_contribution_zipped[m_id]);
			i++;
		}

		sort(selectedMachines, cost_per_machine);

		if (selectedMachines.size() < machines_to_keep)
			return selectedMachines;
		else {
			machines_to_keep /= 2;
			int MS = selectedMachines.size();
			List<Integer> new_selected_machines = new ArrayList<Integer>();
			int s = 0;
			while (s < machines_to_keep) {
				new_selected_machines.add(selectedMachines.get(s));
				s++;
			}
			while (s > 0) {
				new_selected_machines.add(selectedMachines.get(MS - s));
				s--;
			}

			return new_selected_machines;
		}
	}

}
